﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GDI_应用案例.Pages.Controls
{
    public unsafe class VerificationCode : IDisposable
    {
        private readonly Random _random;
        private int _codeLength = 4;
        private int _noiseLine = 15;
        private int _noisePoint = 50;
        private Font _font;
        private readonly Color[] _warmColors = {
    Color.Red,      // 红色
    Color.Orange,   // 橙色
    Color.Yellow,   // 黄色
    Color.Gold,     // 金色
    Color.Coral,    // 珊瑚色
    Color.Tomato,   // 番茄红
    Color.DarkOrange, // 深橙色
    Color.Salmon,   // 鲑鱼色
    Color.IndianRed, // 印度红
    Color.Chocolate, // 巧克力色
    Color.Peru,     // 秘鲁色
    Color.Sienna,   // 赭色
    Color.DarkSalmon, // 深鲑鱼色
    Color.LightSalmon, // 浅鲑鱼色
    Color.OrangeRed, // 橙红色
    Color.DarkGoldenrod, // 深金色
    Color.Goldenrod // 金麒麟色
};
        public VerificationCode()
        {
            _random = new Random();
            _font = new Font("Arial", 12, FontStyle.Bold);
            CreateWarmColor();
        }
        public int CodeLength
        {
            get => _codeLength;
            set
            {
                if (value <= 0)
                    throw new ArgumentException("长度必须大于零");
                _codeLength = value;
            }
        }
        public int NoiseLine
        {
            get => _noiseLine;
            set
            {
                if (value < 0)
                    throw new ArgumentException("噪音线不能小于零");
                _noiseLine = value;
            }
        }
        public int NoisePoint
        {
            get => _noisePoint;
            set
            {
                if (_noisePoint < 0)
                    throw new ArgumentException("噪音点不能小于零");
                _noisePoint = value;
            }
        }
        public bool NormalCode { get; set; }
        public Font Font
        {
            get => _font;
            set
            {
                if (value == null) return;
                _font?.Dispose();
                _font = value;
            }
        }
        public bool IsDrawNoiseLine { get; set; }
        public bool IsDrawNoisePoint { get; set; }

        public event Action<long> TimeCallback;
        public virtual VerificationCodeResult CreateVerificationCode()
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            string code = CreateCode(CodeLength);
            SizeF size = TextRenderer.MeasureText(code, Font);
            Bitmap bitmap = new Bitmap((int)(size.Width + 10.5f), (int)(size.Height + 10.5f));
            Graphics g = Graphics.FromImage(bitmap);
            g.Smooth();
            //g.Clear(Color.White);
            // 绘制背景
            DrawBackColor(bitmap, g);
            // 绘制噪音线
            if (IsDrawNoiseLine)
                DrawNoiseLine(bitmap, g);
            // 绘制验证码
            DrawCode(bitmap, g, code);
            // 绘制噪音点
            if (IsDrawNoisePoint)
                DrawNoisePoint(bitmap);
            // 绘制边框线
            //DrawRectangle(bitmap, g);

            g.Dispose();
            stopwatch.Stop();
            TimeCallback?.Invoke(stopwatch.ElapsedMilliseconds);
            return new VerificationCodeResult
            {
                Code = code,
                Image = bitmap
            };
        }
        protected virtual void DrawBackColor(Bitmap bitmap, Graphics g)
        {
            var rect = new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1);
            var color = CreateRandomWarmColor();
            Brush brush = new LinearGradientBrush(rect, color.StartColor, color.EndColor, RandomMode);
            g.FillRectangle(brush, rect);
            brush.Dispose();
        }
        private Colors CreateRandomWarmColor() => CreateWarmColor(_random.Next(_warmColors.Length - 1));
        private Colors CreateWarmColor(int index)
        {
            if (_warmColorArray == null)
                CreateWarmColor();

            fixed (Colors* cptr= _warmColorArray)
            {
                return *(cptr + index);
            }
           
        }
        Colors[] _warmColorArray;
        private void CreateWarmColor()
        {
            List<Colors> colors = new List<Colors>(_warmColors.Length << 1);
            fixed (Color* cptr = _warmColors)
            {
                for (int i = 0; i < _warmColors.Length; i++)
                {
                    for (int j = i + 1; j < _warmColors.Length; j++)
                    {
                        float brightnessDiff = Math.Abs((cptr + i)->GetBrightness() - (cptr + j)->GetBrightness());
                        if (brightnessDiff < 0.3f)
                            colors.Add(new Colors
                            {
                                StartColor = _warmColors[i],
                                EndColor = _warmColors[j]
                            });
                    }
                }
            }
            _warmColorArray= colors.ToArray();
        }
        protected virtual void DrawRectangle(Bitmap bitmap, Graphics g)
        {
            Pen pen = new Pen(RandomColor());
            g.DrawRectangle(pen, 0, 0, bitmap.Width - 1, bitmap.Height - 1);
            pen.Dispose();
        }
        protected virtual void DrawCode(Bitmap bitmap, Graphics g, string code)
        {
            if (NormalCode)
                DrawNormalCode(bitmap, g, code);
            else
                DrawObliqueCode(bitmap, g, code);
        }
        private void DrawNormalCode(Bitmap bitmap, Graphics g, string code)
        {
            var rect = new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1);
            Brush brush = new LinearGradientBrush(rect, RandomColor(), RandomColor(), RandomMode);
            g.DrawString(code, Font, brush, rect);
            brush.Dispose();
        }
        private void DrawObliqueCode(Bitmap bitmap, Graphics g, string code)
        {
            var rect = new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1);
            // 设置文字格式为单行
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;
            float x = rect.X;
            float y = rect.Y;
            float maxCharWidth = rect.Width / code.Length; // 每个字符的最大宽度
            foreach (char c in code)
            {
                float angle = _random.Next(-60, 60);
                var charRect = new RectangleF(x, y, maxCharWidth, rect.Height);
                using (Matrix matrix = new Matrix())
                {
                    matrix.RotateAt(angle, new PointF(charRect.Left + charRect.Width / 2, charRect.Top + charRect.Height / 2));
                    g.Transform = matrix;
                    var colorStart = RandomColor();
                    var colorEnd = RandomColor();
                    var mode = RandomMode;
                    RectangleF shadowRect = new RectangleF(charRect.X + _random.Next(2, 4), charRect.Y + _random.Next(2, 4), charRect.Width, charRect.Height);
                    Brush shadowBrush = new LinearGradientBrush(shadowRect, Color.FromArgb(100, colorStart), Color.FromArgb(100, colorEnd), mode);
                    Brush brush = new LinearGradientBrush(charRect, colorStart, colorEnd, mode);
                    g.DrawString(c.ToString(), Font, shadowBrush, shadowRect, format);
                    g.DrawString(c.ToString(), Font, brush, charRect, format);
                    g.ResetTransform();

                    brush.Dispose();
                    shadowBrush.Dispose();
                }
                x += maxCharWidth;
            }
            format.Dispose();
        }
        protected virtual void DrawNoisePoint(Bitmap bitmap)
        {
            int width = bitmap.Width;
            int height = bitmap.Height;
            BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            try
            {
                byte* scan0 = (byte*)bitmapData.Scan0;
                int stride = bitmapData.Stride;
                Parallel.For(0, NoisePoint, i =>
                {
                    int x = _random.Next(width);
                    int y = _random.Next(height);
                    byte* pixel = scan0 + y * stride + (x << 2);
                    byte[] bytes = new byte[4];
                    _random.NextBytes(bytes);
                    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, (IntPtr)pixel, bytes.Length);
                });
            }
            finally
            {
                bitmap.UnlockBits(bitmapData);
            }
        }
        protected LinearGradientMode RandomMode => (LinearGradientMode)_random.Next(4);
        protected virtual void DrawNoiseLine(Bitmap bitmap, Graphics g)
        {
            Pen[] pens = new Pen[NoiseLine];
            Point* pointsPtr = stackalloc Point[NoiseLine << 1];
            int width = bitmap.Width;
            int height = bitmap.Height;

            fixed (Pen* penPtr = pens)
            {
                Parallel.For(0, NoiseLine, i =>
                {
                    // 随机生成颜色和透明度
                    pens[i] = new Pen(RandomColor(_random.Next(50, 255)), _random.Next(1, 5));
                    // 随机生成线条的起点和终点
                    Point* tempPtr = pointsPtr + (i << 1);
                    *tempPtr = RandomPoint(width, height) + new Size(_random.Next(-5, 5), _random.Next(-5, 5));
                    *(tempPtr + 1) = RandomPoint(width, height) + new Size(_random.Next(-5, 5), _random.Next(-5, 5));
                });

                for (int i = 0; i < NoiseLine; i++)
                {
                    Point* tempPtr = pointsPtr + (i << 1);
                    Pen* tempPenPtr = penPtr + i;
                    var pen = new Pen(Color.FromArgb(50, Color.Black), tempPenPtr->Width);
                    g.DrawLine(pen, *tempPtr + new Size(_random.Next(1, 3), _random.Next(1, 3)), *(tempPtr + 1) + new Size(_random.Next(1, 3), _random.Next(1, 3)));
                    g.DrawLine(*tempPenPtr, *tempPtr, *(tempPtr + 1));
                    pen.Dispose();
                    tempPenPtr->Dispose();
                }
            }
        }
        protected Point RandomPoint(int xMax, int yMax) => new Point(_random.Next(xMax), _random.Next(yMax));
        protected Color RandomColor(int alpha = 255)
        {
            byte[] bytes = new byte[3];
            _random.NextBytes(bytes);
            fixed (byte* bytesPtr = bytes)
            {
                byte* p = bytesPtr;
                Color color = Color.FromArgb(alpha, *(p++), *(p++), *p);
                return color;
            }
        }

        protected virtual string CreateCode(int length)
        {
            byte[] randomBytes = new byte[length];
            _random.NextBytes(randomBytes);
            fixed (byte* bytesPtr = randomBytes)
            {
                char* buffer = stackalloc char[length];
                for (int i = 0; i < length; i++)
                {
                    byte randomByte = *(bytesPtr + i);
                    if ((randomByte & 1) == 1)
                        *(buffer + i) = (char)('0' + (randomByte % 10));
                    else
                        *(buffer + i) = (char)('A' + (randomByte % 26));
                }
                return new string(buffer, 0, length);
            }
        }

        public void Dispose()
        {
            Font?.Dispose();
        }
        ~VerificationCode()
        {
            Dispose();
        }
    }
    public class VerificationCodeResult
    {
        public Image Image { get; set; }
        public string Code { get; set; }
    }
    internal struct Colors
    {
        public Color StartColor { get; set; }
        public Color EndColor { get; set; }
    }
}
